//
// Created by LiuYou on 2021/7/3.
//

#include <cstdlib>
#include <fcntl.h>
#include <cstdio>
#include <unistd.h>
#include <cerrno>
#include <algorithm>
#include <poll.h>

// 两个设备间通信的程序。
#define TTY1        "/dev/tty11"
#define TTY2        "/dev/tty12"
#define BUF_SIZE    1024


enum {
    STATE_R = 1,
    STATE_W,
    STATE_AUTO,
    STATE_EX,
    STATE_T
};


struct RelayerFSM {
    int state;
    int sfd;
    int dfd;
    char buf[BUF_SIZE]{};
    int len;
    int pos;
    char* err;
};


static void fsmDriver(struct RelayerFSM* fsm) {
    switch (fsm->state) {
        case STATE_R:
            fsm->len = read(fsm->sfd, fsm->buf, BUF_SIZE);
            if (fsm->len == 0) {
                fsm->state = STATE_T;
            } else if (fsm->len < 0) {
                if (errno == EAGAIN) {
                    fsm->state = STATE_R;
                } else {
                    fsm->err = "read()";
                    fsm->state = STATE_EX;
                }
            } else {
                fsm->pos = 0;
                fsm->state = STATE_W;
            }
            break;
        case STATE_W: {
            ssize_t ret = write(fsm->dfd, fsm->buf + fsm->pos, fsm->len);
            if (ret < 0) {
                if (errno == EAGAIN) {
                    fsm->state = STATE_W;
                } else {
                    fsm->err = "write()";
                    fsm->state = STATE_EX;
                }
            } else {
                fsm->pos += ret;
                fsm->len -= ret;
                if (fsm->len == 0) {
                    fsm->state = STATE_R;
                } else {
                    fsm->state = STATE_W;
                }
            }
        }
            break;
        case STATE_EX:
            std::perror(fsm->err);
            fsm->state = STATE_T;
            break;
        case STATE_T:
            // terminate
            break;
        default:
            abort();
            break;
    }

}


static void relay(int fd1, int fd2) {
    // 将两个文件描述符都改为非阻塞。
    int fd1Save = fcntl(fd1, F_GETFL);
    fcntl(fd1, F_SETFL, fd1Save | O_NONBLOCK);
    int fd2Save = fcntl(fd2, F_GETFL);
    fcntl(fd2, F_SETFL, fd2Save | O_NONBLOCK);

    struct RelayerFSM fsm12;
    struct RelayerFSM fsm21;
    fsm12.state = STATE_R;
    fsm12.sfd = fd1;
    fsm12.dfd = fd2;

    fsm21.state = STATE_R;
    fsm21.sfd = fd2;
    fsm21.dfd = fd1;

//    // TODO 注意: 为什么是盲等, 因为使用了while循环。
//    while (fsm12.state != STATE_T || fsm21.state != STATE_T) {
//        fsmDriver(&fsm12);
//        fsmDriver(&fsm21);
//    }


    pollfd pollFD[2];
    pollFD[0].fd = fd1;
    pollFD[1].fd = fd2;

    // TODO 注意: 改进
    while (fsm12.state != STATE_T || fsm21.state != STATE_T) {
        // 布置监视任务。
        pollFD[0].events = 0;

        if (fsm12.state == STATE_R) {
            pollFD[0].events |= POLLIN;
        }
        if (fsm21.state == STATE_W) {
            pollFD[0].events |= POLLOUT;
        }

        pollFD[1].events = 0;
        if (fsm12.state == STATE_W) {
            pollFD[1].events |= POLLOUT;
        }
        if (fsm21.state == STATE_R) {
            pollFD[1].events |= POLLIN;
        }
        // 监视
        if (fsm12.state < STATE_AUTO || fsm21.state < STATE_AUTO) {
            while (poll(pollFD, 2, -1) < 0) {
                if (errno == EINTR) {
                    continue;
                }
                std::perror("poll()");
                std::exit(1);
            }
        }
        // 查看监视结果
        if (pollFD[0].revents & POLLIN || pollFD[1].revents & POLLOUT ||
            fsm12.state > STATE_AUTO)
            fsmDriver(&fsm12);
        if (pollFD[1].revents & POLLIN || pollFD[0].revents & POLLOUT ||
            fsm21.state > STATE_AUTO)
            fsmDriver(&fsm21);
    }

    fcntl(fd1, F_SETFL, fd1Save);
    fcntl(fd2, F_SETFL, fd2Save);

}


int main() {
    int fd1 = open(TTY1, O_RDWR);
    if (fd1 < 0) {
        std::perror("open()");
        std::exit(1);
    }
    write(fd1, "TTY1\n", 5);
    int fd2 = open(TTY2, O_RDWR | O_NONBLOCK);
    if (fd2 < 0) {
        std::perror("open()");
        std::exit(1);
    }
    write(fd2, "TTY2\n", 5);

    relay(fd1, fd2);

    close(fd2);
    close(fd1);
    std::exit(0);
}